#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h> //无线更新固件
#include <WiFiUdp.h>
#include "WiFiManager.h"
#include "ArduinoJson.h"
#include <FastLED.h>
#include <NTPClient.h>
#include <LittleFS.h>

WiFiManager wifim; // WiFi配网
ESP8266WebServer server(80);  // Web服务
ESP8266HTTPUpdateServer httpUpdater;  // 无线更新固件
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 3600 * 8, 60000);       //乘以8是因为北京时间时东八区的时间
constexpr unsigned long HTTP_TIMEOUT = 5000;               // 服务器的最大响应时间
//********************WS2812B配置*******************//
#define NUM_LEDS 58 /// 灯的数量
#define DATA_PIN 14 /// 输出控制信号引脚 ESP8266 GPIO_13
#define CONFIG_FILE "/config.json"
#define LED_TYPE WS2812B         // LED灯带型号
#define COLOR_ORDER GRB         // RGB灯珠中红色、绿色、蓝色LED的排列顺序
//
String wifi_ip = "192.168.4.1";
String wd = "2.0";
int ledbright = 10; // LED亮度控制变量

CRGB leds[NUM_LEDS];    // leds
CRGB OFF = CRGB::Black; // 黑色
CRGB timecolor = CRGB::Green;
bool dian = true;  // 中间闪烁点

int nums[10][14] = {
        {1, 2, 3,  4, 5,  6, 9,  10, 11, 12, 13, 14},
        {5, 6, 13, 14},
        {3, 4, 5,  6, 7,  8, 9,  10, 11, 12},
        {3, 4, 5,  6, 7,  8, 11, 12, 13, 14},
        {1, 2, 5,  6, 7,  8, 13, 14},
        {1, 2, 3,  4, 7,  8, 11, 12, 13, 14},
        {1, 2, 3,  4, 7,  8, 9,  10, 11, 12, 13, 14},
        {3, 4, 5,  6, 13, 14},
        {1, 2, 3,  4, 5,  6, 7,  8,  9,  10, 11, 12, 13, 14},
        {1, 2, 3,  4, 5,  6, 7,  8,  11, 12, 13, 14},
};

// 加载 config
void loadconfig();
// 保存数据到 config
void saveconfig();
// Web 获取 数据 GET
void serverget();
// Web 设置 亮度和颜色 POST
void serverset();
// 主页面
void handleServeRoot();
// 空页面提示
void handleNotFound();

void setup() {
    // 初始化 串口输出 波特率
    Serial.begin(115200);
    // 初始化config
    if (!LittleFS.begin()) {
        Serial.println("LittleFS初始化失败！");
        return;
    }
    // 延迟
    delay(100);
    // 设置联网延迟 120秒
    wifim.setConnectTimeout(120);                      // 等待wifi 延迟 推荐1分钟
    // 如果没连上网络则 启动陪网热点
    wifim.autoConnect("OLED配网热点");
    //初始化网络
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.print("ESP8266 配网完成->");
    wifi_ip = WiFi.SSID();
    Serial.println(WiFi.SSID());              // WiFi名称
    delay(100);
    // 时间服务器初始化 NTP
    timeClient.begin();
    delay(100);
    Serial.println("时间服务器启动！");
    // ws2812b灯带
    LEDS.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);  // 初始化光带
    LEDS.setBrightness(ledbright);

    delay(100);
    // -----------------------------
    Serial.print("网页更新 -> http://");
    Serial.print(WiFi.localIP());
    Serial.println("/update");
    //==========================================
    server.on("/",HTTP_GET, handleServeRoot);
    server.on("/get",HTTP_GET,serverget);
    server.on("/set",HTTP_POST,serverset);
    server.onNotFound(handleNotFound); // 处理未找到的页面
    // 网页更新固件
    httpUpdater.setup(&server);
    server.begin();
    // 从EEPROM获取数据
    loadconfig(); // 配置项
}

void loadconfig() {
    if (File configFile = LittleFS.open(CONFIG_FILE, "r")) {
        const size_t size = configFile.size();
        std::unique_ptr<char[]> buf(new char[size]);
        configFile.readBytes(buf.get(), size);
        configFile.close();
        JsonDocument doc;
        DeserializationError error = deserializeJson(doc, buf.get());
        if (!error) {
            ledbright = doc["brightness"];
            timecolor.r = doc["r"];
            timecolor.g = doc["g"];
            timecolor.b = doc["b"];
        }
    }
}

void saveconfig() {
    JsonDocument doc;
    doc["brightness"] = ledbright;
    // 正确创建嵌套对象的方法
    doc["r"] = timecolor.r;
    doc["g"] = timecolor.g;
    doc["b"] = timecolor.b;

    if (File configFile = LittleFS.open(CONFIG_FILE, "w")) {
        serializeJson(doc, configFile);
        configFile.close();
    }
}

void ws2812_show(const int h, const int m) {
    FastLED.clear();
    if (h / 10 != 0) {
        for (const int i: nums[h / 10]) {
            if (i != 0) {
                leds[i - 1] = timecolor;
            }
        }
    }

    for (const int i: nums[h % 10]) {
        if (i != 0) {
            leds[i + 13] = timecolor;
        }
    }

    for (const int j: nums[m / 10]) {
        if (j != 0) {
            leds[j + 29] = timecolor;
        }
    }
    for (const int j: nums[m % 10]) {
        if (j != 0) {
            leds[j + 43] = timecolor;
        }
    }

}

void loop() {
    timeClient.update();
    //监听客户请求并处理
    server.handleClient();
    // _______________________________
    EVERY_N_SECONDS(1) {
        dian = !dian;
        if (dian) {
            leds[28] = timecolor;
            leds[29] = timecolor;
        } else {
            leds[28] = OFF;
            leds[29] = OFF;
        }
    }
    EVERY_N_SECONDS(10) {
        int h = timeClient.getHours();
        int m = timeClient.getMinutes();
        ws2812_show(h, m);
    }
    FastLED.show();
}

void serverset() {
    // 检查请求中是否包含期望的参数
    if (server.hasArg("r") && server.hasArg("g") && server.hasArg("b") && server.hasArg("brightness")) {
        timecolor.r = server.arg("r").toInt();
        timecolor.g = server.arg("g").toInt();
        timecolor.b = server.arg("b").toInt();
        ledbright = server.arg("brightness").toInt();
        // d = server.arg("effect").toCharArray();


        // 应用新的颜色和亮度
        LEDS.setBrightness(ledbright);

        // 保存配置到LittleFS
        saveconfig();

        // 返回成功响应
        server.send(200, "application/json", "{\"success\":true}");
    } else {
        server.send(400, "text/plain", "Missing parameters");
    }
}

void serverget() {
    JsonDocument doc;
    doc["r"] = timecolor.r;
    doc["g"] = timecolor.g;
    doc["b"] = timecolor.b;
    doc["brightness"] = ledbright;

    String response;
    serializeJson(doc, response);
    server.send(200, "application/json", response);
}


void handleServeRoot() {
    File file = LittleFS.open("/index.html", "r"); // 假设网页存放在LittleFS中
    if (file) {
        server.streamFile(file, "text/html");
        file.close();
    } else {
        server.send(500, "text/plain", " LittleFS 无法打开HTML文件");
    }
}

void handleNotFound() {
    server.send(404, "text/plain", "404: Not found");
}